#ifndef __PLANNERPREQUE_HPP__
#define __PLANNERPREQUE_HPP__

#include "globaldef.hpp"
#include "Map.hpp"
#include "Hyperparameters.hpp"
#include "Berth.hpp"
#include "Boat.hpp"
#include "Robot.hpp"
#include "Goods.hpp"
#include "PlanForLand.hpp"
#include "PlanForSea.hpp"
#include "Planner.hpp"
#include <cstdio>
#include <vector>
#include <queue>
#include <utility>
#include <climits>

struct Dist//目的货物和目的泊位
{
    /* data */
    Goods *goods=nullptr;
    int BerthID=-1;
    bool operator<(const Dist &rhs) const{
        return goods->value<rhs.goods->value;
    }
};


class PlannerPreQue : public Planner {
private:
    int frameID = -1; 
    const Map *map = nullptr; 
    std::vector<Berth> *berths = nullptr; 
    const std::vector<Goods *> *goods_list = nullptr; 
    const std::vector<BoatDispatcher> *boatDispatchers = nullptr;
    const std::vector<RobotDispatcher> *robotDispatchers = nullptr;
    std::priority_queue<std::pair<int,Dist>> goodsqueue[BERTH_NUM];//泊位优先队列

public:
    /**
     * @brief 初始化
     * @param map 地图
     * @param berths 泊位列表
     * @param goods_list 货物列表
     * @param boatDispatchers 船调度器列表
     * @param robotDispatchers 机器人调度器列表
     * @note 初始化阶段调用一次，连接到可用的地图和泊位、货物列表等，由外部保证这些数据实时刷新
     */
    void init(const Map *map, std::vector<Berth> *berths, const std::vector<Goods *> *goods_list,
        const std::vector<BoatDispatcher> *boatDispatchers,
        const std::vector<RobotDispatcher> *robotDispatchers) override {
        this->map = map;
        this->berths = berths;
        this->goods_list = goods_list;
        this->boatDispatchers = boatDispatchers;
        this->robotDispatchers = robotDispatchers;
    }
    /**
     * @brief 每生成一个goods，就更新每个泊位的goodsqueue
     * @param goods 新生成的货物
     */
    void refresh(int frameID, int newGoodsNum) override{
        if(newGoodsNum == 0) return;
        auto n = (*goods_list).size();
        auto it = (*goods_list).rbegin(); 
        for(int k=n-newGoodsNum;k<n&&k>=0;++k){
            Dist dist;
            dist.goods = *it;
            for(int i=0;i<BERTH_NUM;++i){//遍历每个起始泊位
                int max = INT_MIN;
                float value = 0;
                int toGoodsDistance = (*map).getBerthDistance(i,*(dist.goods));
                for(int j=0;j<BERTH_NUM;++j){//遍历每个目的泊位
                    int distance = toGoodsDistance + (*map).getBerthDistance(j,*(dist.goods));
                    value =  static_cast<float>((dist.goods)->value)/distance + (*berths)[j].getValue();//计算价值 berths[j].getValue()为泊位当前的激励
                    if(value>max){
                        max = value;
                        dist.BerthID = j;
                    }           
                }
                goodsqueue[i].push({value,dist});//更新起始泊位的goodsqueue
            }
            ++it;         
        }
        this->frameID = frameID;
    }
    /**
     * @brief 根据空闲机器人当前泊位选择目的货物和目的泊位
     * 
     * @param curBerthID 当前泊位ID
     */
    PlanForLand allocatePlan(const Robot *robot) override{
        int curBerthID = (*map).getNearestBerth(*robot);
        while(!goodsqueue[curBerthID].empty()){//队列不为空 遍历队列
            Dist dist = goodsqueue[curBerthID].top().second;
            goodsqueue[curBerthID].pop();
            if(!dist.goods->isCarried){   //找到最优先未被其他机器人设为目标的货物 并创建plan返回
                return PlanForLand(dist.goods,dist.BerthID);
            }
        }
        return PlanForLand();//遍历完队列都无有效货物，返回无效plan
    }

    /**
     * @brief 给指定船分配任务
     * @param boat 指定船
     * @return PlanForSea 分配的任务
     */
    PlanForSea allocatePlan(const Boat *boat) override {
        int berthID = random() % BERTH_NUM;
        // 时间 = 当前帧 + 到达港口时间 + 该泊位把船装满所需时间
        int time = frameID + (*berths)[berthID].distance + boat->volume / (*berths)[berthID].velocity;
        return PlanForSea(berthID, time);
    }
};

#endif // __PLANNERPREQUE_HPP__